package ms.unicorn.modellanguage;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import ms.unicorn.Element;
import ms.unicorn.Relation;
import ms.unicorn.diagram.providers.UnicornElementTypes;
import ms.unicorn.util.ConfigUtil;
import ms.unicorn.util.TypeConvert;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.swt.SWT;

public class LanguageRelation extends LanguageElement {

	public static final int LINK_CONSTRAINT_ALLOW = 0;
	public static final int LINK_CONSTRAINT_DISALLOW = 1;
	public static final int LINK_CONSTRAINT_INHERITED = 2;
	
	public static class LinkConstraint {
		public int type = LINK_CONSTRAINT_INHERITED;
		
		public final List<String> Sources = new ArrayList<String>();
		public final List<String> Targets = new ArrayList<String>();
		
		public LinkConstraint() {

		}

		public LinkConstraint(IConfigurationElement config) {
			load(config);
		}

		public int checkConstraint(String se, String te) {
			int result = LINK_CONSTRAINT_INHERITED;
			boolean allowSource = (Sources.size() == 0 || se == null || Sources.indexOf(se) >= 0);
			boolean allowTarget = (Targets.size() == 0 || te == null || Targets.indexOf(te) >= 0);
			if (allowSource && allowTarget)
				result = type;
			return result;
		}
	
		public LinkConstraint load(IConfigurationElement config) {
			type = ConfigUtil.get(config, "type", "allow").equalsIgnoreCase("allow") ? LanguageRelation.LINK_CONSTRAINT_ALLOW :
				LanguageRelation.LINK_CONSTRAINT_DISALLOW;
			
			toStringList(ConfigUtil.get(config, "sources", "*"), Sources);
			toStringList(ConfigUtil.get(config, "targets", "*"), Targets);
			return this;
		}

		private void toStringList(String string, List<String> list) {
			for(String id: string.split(",")) {
				if (id.trim().length() > 0 && !id.equalsIgnoreCase("*"))
					list.add(id);
			}
		}
		
		
	}
	
	public static class RelationStyle extends LinkConstraint {

		private int lineWidth = 1;
		private int lineStyle = SWT.LINE_SOLID;
		
		public final DecoratorProxyList decorators;
		
		public RelationStyle() {
			type = LINK_CONSTRAINT_ALLOW;
			decorators = new DecoratorProxyList();
		}
		
		public RelationStyle(IConfigurationElement config) {
			this();
			load(config);
		}
		
		@Override
		public RelationStyle load(IConfigurationElement config) {
			super.load(config);
			lineWidth = TypeConvert.toInt(ConfigUtil.get(config, "linewidth", "1"), 1);
			setLineStyle(ConfigUtil.get(config, "linestyle", "solid"));
			decorators.load(config.getChildren("decorator"));
			return this;
		}

		public void setLineWidth(int lineWidth) {
			this.lineWidth = lineWidth;
		}

		public int getLineWidth() {
			return lineWidth;
		}

		public void setLineStyle(String styleName) {
			if (lineStyles.containsKey(styleName))
				lineStyle = lineStyles.get(styleName);
		}
		
		public void setLineStyle(int lineStyle) {
			this.lineStyle = lineStyle;
		}

		public int getLineStyle() {
			return lineStyle;
		}

	}
	
	private List<LinkConstraint> linkContraints;
	private List<RelationStyle> relationStyles;
	
	private IElementEditHelperAdvice advice;
	private RelationStyle defaultStyle;
	
	static Map<String, Integer> lineStyles = new TreeMap<String, Integer>();
	static Map<String, Integer> lineCaps = new TreeMap<String, Integer>();
	static Map<String, Integer> lineJoins = new TreeMap<String, Integer>();
	
	static {
		lineStyles.put("solid", SWT.LINE_SOLID);
		lineStyles.put("dash", SWT.LINE_DASH);
		lineStyles.put("dashdot", SWT.LINE_DASHDOT);
		lineStyles.put("dashdotdot", SWT.LINE_DASHDOTDOT);
		lineStyles.put("dot", SWT.LINE_DOT);
		
		lineCaps.put("flat", SWT.CAP_FLAT);
		lineCaps.put("round", SWT.CAP_ROUND);
		lineCaps.put("square", SWT.CAP_SQUARE);
		
		lineJoins.put("bevel", SWT.JOIN_BEVEL);
		lineJoins.put("miter", SWT.JOIN_MITER);
		lineJoins.put("round", SWT.JOIN_ROUND);
	}

	
	public LanguageRelation(IModelLanguage language) {
		super(language);
		linkContraints = new ArrayList<LinkConstraint>();
		relationStyles = new ArrayList<RelationStyle>();
		defaultStyle = new RelationStyle();
	}
	
	public LanguageRelation(IModelLanguage language, IConfigurationElement config) {
		this(language);
		load(config);
	}
	
	@Override
	protected void load(IConfigurationElement config) {
		super.load(config);
		defaultStyle.lineWidth = ConfigUtil.getInt(config, "linewidth", 1);
		defaultStyle.lineStyle = ConfigUtil.getInt(config, "linestyle", SWT.LINE_SOLID, lineStyles);
	}
	
	@Override
	protected void processConfigItem(IConfigurationElement config,
			String itemName) {
		if ("relationStyle".equalsIgnoreCase(itemName))
			relationStyles.add(new RelationStyle(config));
		else if ("relationConstraint".equalsIgnoreCase(itemName))
			linkContraints.add(new LinkConstraint(config));
		else
			super.processConfigItem(config, itemName);
	}
	
	@Override
	protected IElementType getBaseType() {
		return UnicornElementTypes.Relation_4001; 
	}
	
	public void addRelationConstraint(LinkConstraint lc) {
		linkContraints.add(lc);
	}
	
	public void addRelationConstraint(IConfigurationElement config) {
		LinkConstraint lc = new LinkConstraint();
		lc.load(config);
		linkContraints.add(lc);
	}
	
	public void addRelationStyle(IConfigurationElement config) {
		RelationStyle style = new RelationStyle();
		style.load(config);
		relationStyles.add(style);
	}
	
	public boolean checkRelationConstraints(Element container,
			Relation linkInstance, Element source, Element target) {
		int constraint = LINK_CONSTRAINT_INHERITED;
		
		String se = source != null ? source.getElementType() : null;
		String te = target != null ? target.getElementType() : null;
		
		for (LinkConstraint lc: linkContraints) {
			int tempResult = lc.checkConstraint(se, te);
			if (tempResult != LINK_CONSTRAINT_INHERITED)
				constraint = tempResult;
			if (constraint == LINK_CONSTRAINT_DISALLOW)
				break;
		}
		
		boolean result = constraint == LINK_CONSTRAINT_ALLOW;
		
		IElementEditHelperAdvice helper = getEditHelperAdvice();
		if (result && helper instanceof LanguageRelationEditHelperAdvice) {
			result = ((LanguageRelationEditHelperAdvice) helper).checkRelationConstraints(container,
					linkInstance, source, target);
		}
		
		return result;
	}
	
	public RelationStyle getRelationStyle(Element source, Element target) {
		if (source == null || target == null)
			return defaultStyle;
		String se = source.getElementType();
		String te = target.getElementType();
		for (RelationStyle rs: relationStyles)
			if (rs.checkConstraint(se, te) == LINK_CONSTRAINT_ALLOW) return rs;
		return defaultStyle;
	}
	
	public void setEditHelperAdvice(IElementEditHelperAdvice advice) {
		this.advice = advice;
	}

	public IElementEditHelperAdvice getEditHelperAdvice() {
		return advice != null ? advice : LanguageRelationEditHelperAdvice.DefaultInstance;
	}


	public RelationStyle getDefaultStyle() {
		return defaultStyle;
	}
}
